﻿#define LOOSE_MPEG

using System;
using System.Collections.Generic;
using System.Text;

namespace MPEGnet
{
    internal struct MBlockInfo
    {
        public bool skipped;
        public bool mb_intra;

        public int recon_right_for_prev;              /* Past right forw. vector.         */
        public int recon_down_for_prev;               /* Past down forw. vector.          */
        public int recon_right_back_prev;             /* Past right back vector.          */
        public int recon_down_back_prev;              /* Past down back vector.           */

        public bool bpict_past_forw;                  /* Past B frame forw. vector flag.  */
        public bool bpict_past_back;                  /* Past B frame back vector flag.   */

        public bool mb_motion_forw;
        public bool mb_motion_back;

        public MBlockInfo(ref Macroblock mblock, bool p)
        {
            mb_intra = mblock.mb_intra;

            recon_right_for_prev = mblock.recon_right_for_prev;
            recon_down_for_prev = mblock.recon_down_for_prev;
            recon_right_back_prev = mblock.recon_right_back_prev;
            recon_down_back_prev = mblock.recon_down_back_prev;

            bpict_past_back = mblock.bpict_past_back;
            bpict_past_forw = mblock.bpict_past_forw;

            skipped = p;
            mb_motion_forw = mb_motion_back = false;
        }
    }

    internal struct BlockInfo
    {
        public DCTBLOCK dct_recon;
        public bool needs_idct;
        public bool sparse_idct;
        public int sparse_idct_index;
        public short sparse_idct_coef;

        public bool zero_block_flag;

        public int recon_right_for;
        public int recon_down_for;
        public int recon_right_back;
        public int recon_down_back;


        static int[] zigzagOrder =
        {
            0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12,
            19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35,
            42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51,
            58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63
        };

        internal void SetReconInfo(bool zero_block_flag, int recon_right_for, int recon_down_for, int recon_right_back, int recon_down_back)
        {
            this.zero_block_flag = zero_block_flag;
            this.recon_right_for = recon_right_for;
            this.recon_down_for = recon_down_for;
            this.recon_right_back = recon_right_back;
            this.recon_down_back = recon_down_back;
        }

        internal void SetDct(DCTBLOCK blk, bool sparse, int pos)
        {
            sparse = false;
            this.needs_idct = true;
            this.sparse_idct = sparse;

            if (sparse)
            {
                //this.sparse_idct_coef = zigzagCoefs[zigzagOrder[pos]];
                this.sparse_idct_index = zigzagOrder[pos];
                dct_recon = blk;
            }
            else
                dct_recon = blk;
        }
    }

    internal class Reconstructor
    {
        int _mb_width;
        int _mb_height;

        public bool QualityFlag { get; set; }
        public BlockInfo[] Blocks { get; private set; }
        public MBlockInfo[] Macroblocks { get; private set; }

        public Reconstructor(int mb_width, int mb_height)
        {
            _mb_width = mb_width;
            _mb_height = mb_height;

            Macroblocks = new MBlockInfo[mb_width * mb_height];
            Blocks = new BlockInfo[mb_width * mb_height * 6];
        }

        public void ReconBlock(int addr, int bnum, ref MBlockInfo mblockInfo, ref BlockInfo blockInfo,
            PictImage current, PictImage past, PictImage future, int picture_code_type)
        {
            int blockAddress = addr * 6 + bnum;

            if (!blockInfo.zero_block_flag && blockInfo.needs_idct)
            {
                blockInfo.needs_idct = false;
                // Perform IDCT
                if (blockInfo.sparse_idct)
                    jrevdct.j_rev_dct_sparse(ref blockInfo.dct_recon, blockInfo.sparse_idct_index);
                else
                    jrevdct.j_rev_dct(ref blockInfo.dct_recon);
            }

            if (mblockInfo.mb_intra)
                ReconIMBlock(addr, current, ref Blocks[blockAddress].dct_recon, bnum);
            else
            {

                if (mblockInfo.mb_motion_forw && mblockInfo.mb_motion_back)
                {
                    //using (PDB.Trace("ReconBi"))
                    ReconBiMBlock(addr, current, past, future, ref blockInfo.dct_recon, bnum, blockInfo.recon_right_for, blockInfo.recon_down_for,
                            blockInfo.recon_right_back, blockInfo.recon_down_back, blockInfo.zero_block_flag);
                }
                else if (mblockInfo.mb_motion_forw || (picture_code_type == VidStream.P_TYPE))
                {
                    //using (PDB.Trace("ReconPM"))
                    ReconPMBlock(addr, current, past, future, ref blockInfo.dct_recon, bnum, blockInfo.recon_right_for, blockInfo.recon_down_for, picture_code_type, blockInfo.zero_block_flag);
                }
                else if (mblockInfo.mb_motion_back)
                {
                    //using (PDB.Trace("ReconBM"))
                    ReconBMBlock(addr, current, past, future, ref blockInfo.dct_recon, bnum, blockInfo.recon_right_back, blockInfo.recon_down_back,
                           blockInfo.zero_block_flag);
                }
            }
        }

        public void Reconstruct(ThreadedDispatch dispatch, PictImage current, PictImage past, PictImage future, int picture_code_type, bool full_pel_forw_vector, bool full_pel_back_vector)
        {
            dispatch.Dispatch(0, _mb_width * _mb_height, delegate(int addr)
            {
                MBlockInfo mblockInfo = Macroblocks[addr];
                if (mblockInfo.skipped)
                {
                    if (picture_code_type == VidStream.P_TYPE)
                        ProcessSkippedPFrameMBlock(addr, current, past, future);
                    else if (picture_code_type == VidStream.B_TYPE)
                        ProcessSkippedBFrameMBlock(addr, full_pel_forw_vector, full_pel_back_vector, ref mblockInfo, current, past, future);
                    return;
                }

                for (int bnum = 0; bnum < 6; bnum++)
                    ReconBlock(addr, bnum, ref mblockInfo, ref Blocks[addr * 6 + bnum], current, past, future, picture_code_type);
            });

            /*
            numRecon++;

            using (System.IO.FileStream fs = new System.IO.FileStream("recon" + numRecon + ".raw", System.IO.FileMode.Create, System.IO.FileAccess.Write))
            {
                fs.Write(current.luminance, 0, _mb_width * _mb_height * 16 * 16);
                if(past != null)
                    fs.Write(past.luminance, 0, _mb_width * _mb_height * 16 * 16);
                if(future != null)
                    fs.Write(future.luminance, 0, _mb_width * _mb_height * 16 * 16);
            }
             */
        }

        /*
                 *--------------------------------------------------------------
                 *
                 * ReconSkippedBlock --
                 *
                 *    Reconstructs predictive block for skipped macroblocks
                 *      in B Frames.
                 *
                 * Results:
                 *    No return values.
                 *
                 * Side effects:
                 *    None.
                 *
                 *--------------------------------------------------------------
                 */

        static BlockCombiner8 ReconSkippedBlock8(byte[] sourcePlane,
          int row, int col, int row_size, int right, int down,
          int right_half, int down_half)
        {
            int sourceIndex = ((row + down) * row_size) + col + right;

            if ((right_half == 0) && (down_half == 0))
                return new BlockCombiner8(sourcePlane, sourceIndex);
            else
            {
                int sourceIndex2 = sourceIndex + right_half + (row_size * down_half);
                BlockCombiner8 bc8 = new BlockCombiner8(sourcePlane, sourceIndex);
                bc8.SetAverage(sourceIndex2);
                return bc8;
            }
        }

        static BlockCombiner16 ReconSkippedBlock16(byte[] sourcePlane,
          int row, int col, int row_size, int right, int down,
          int right_half, int down_half)
        {
            int sourceIndex = ((row + down) * row_size) + col + right;

            if ((right_half == 0) && (down_half == 0))
                return new BlockCombiner16(sourcePlane, sourceIndex); //new IntBlock16(sourcePlane, sourceIndex, row_size);
            else
            {
                int sourceIndex2 = sourceIndex + right_half + (row_size * down_half);

                //IntBlock16 sblock = new IntBlock16(sourcePlane, sourceIndex, row_size);
                //IntBlock16 sblock2 = new IntBlock16(sourcePlane, sourceIndex2, row_size);
                //return sblock.Add(sblock2).RightShift(1);

                BlockCombiner16 bc16 = new BlockCombiner16(sourcePlane, sourceIndex);
                bc16.SetAverage(sourceIndex2);
                return bc16;
            }
        }


        /*
         *--------------------------------------------------------------
         *
         * ProcessSkippedPFrameMBlocks --
         *
         *    Processes skipped macroblocks in P frames.
         *
         * Results:
         *    Calculates pixel values for luminance, Cr, and Cb planes
         *      in current pict image for skipped macroblocks.
         *
         * Side effects:
         *    Pixel values in pict image changed.
         *
         *--------------------------------------------------------------
         */

        void ProcessSkippedPFrameMBlock(int addr, PictImage current, PictImage past, PictImage future)
        {
            int row_size, half_row, mb_row, mb_col, row, col;
            int crow, ccol;

            /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

            row_size = (int)(_mb_width << 4);
            half_row = (row_size >> 1);

            /* Calculate macroblock row and col. */

            mb_row = addr / (int)(_mb_width);
            mb_col = addr % (int)(_mb_width);

            /* Calculate upper left pixel row,col for luminance plane. */

            row = mb_row << 4;
            col = mb_col << 4;

            /* For each row in macroblock luminance plane... */

            int currentDestIndex = (row * row_size) + col;
            int futureSrcIndex = (row * row_size) + col;

            for (int rr = 0; rr < 16; rr++)
            {
                Array.Copy(future.luminance, futureSrcIndex, current.luminance, currentDestIndex, 16);
                currentDestIndex += row_size;
                futureSrcIndex += row_size;
            }

            /*
                * Divide row,col to get upper left pixel of macroblock in Cr and Cb
                * planes.
                */

            crow = row >> 1;
            ccol = col >> 1;

            currentDestIndex = (crow * half_row) + ccol;
            futureSrcIndex = (crow * half_row) + ccol;

            /* For each row in Cr, and Cb planes... */
            for (int rr = 0; rr < 8; rr++)
            {
                Array.Copy(future.Cb, futureSrcIndex, current.Cb, currentDestIndex, 8);
                Array.Copy(future.Cr, futureSrcIndex, current.Cr, currentDestIndex, 8);
                currentDestIndex += half_row;
                futureSrcIndex += half_row;
            }
        }


        /*
         *--------------------------------------------------------------
         *
         * ProcessSkippedBFrameMBlocks --
         *
         *    Processes skipped macroblocks in B frames.
         *
         * Results:
         *    Calculates pixel values for luminance, Cr, and Cb planes
         *      in current pict image for skipped macroblocks.
         *
         * Side effects:
         *    Pixel values in pict image changed.
         *
         *--------------------------------------------------------------
         */
        void ProcessSkippedBFrameMBlock(int addr, bool full_pel_forw_vector, bool full_pel_back_vector,
            ref MBlockInfo mblock, PictImage current, PictImage past, PictImage future)
        {
            int row_size, half_row, mb_row, mb_col, row, col;
            int right_half_for = 0, down_half_for = 0;
            int c_right_half_for = 0, c_down_half_for = 0;
            int right_half_back = 0, down_half_back = 0;
            int c_right_half_back = 0, c_down_half_back = 0;
            int right_for = 0, down_for = 0;
            int recon_right_for, recon_down_for;
            int recon_right_back, recon_down_back;
            int right_back = 0, down_back = 0;
            int c_right_for = 0, c_down_for = 0;
            int c_right_back = 0, c_down_back = 0;
            int ccol, crow;
#if LOOSE_MPEG
            int lmaxx = (int)(_mb_width*16-1);
            int lmaxy = (int)(_mb_height*16-1);
            int cmaxx = (int)(_mb_width*8-1);
            int cmaxy = (int)(_mb_height*8-1);
#endif

            /* Calculate row sizes for luminance and Cr/Cb macroblock areas. */

            row_size = (int)(_mb_width << 4);
            half_row = (row_size >> 1);

            /* Establish motion vector codes based on full pixel flag. */
            if (full_pel_forw_vector)
            {
                recon_right_for = mblock.recon_right_for_prev << 1;
                recon_down_for = mblock.recon_down_for_prev << 1;
            }
            else
            {
                recon_right_for = mblock.recon_right_for_prev;
                recon_down_for = mblock.recon_down_for_prev;
            }

            if (full_pel_back_vector)
            {
                recon_right_back = mblock.recon_right_back_prev << 1;
                recon_down_back = mblock.recon_down_back_prev << 1;
            }
            else
            {
                recon_right_back = mblock.recon_right_back_prev;
                recon_down_back = mblock.recon_down_back_prev;
            }

            /* If only one motion vector, do display copy, else do full
               calculation. 
            */
            /* Calculate motion vectors. */

            if (mblock.bpict_past_forw)
            {
                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                recon_right_for /= 2;
                recon_down_for /= 2;
                c_right_for = recon_right_for >> 1;
                c_down_for = recon_down_for >> 1;
                c_right_half_for = recon_right_for & 0x1;
                c_down_half_for = recon_down_for & 0x1;
            }
            if (mblock.bpict_past_back)
            {
                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                recon_right_back /= 2;
                recon_down_back /= 2;
                c_right_back = recon_right_back >> 1;
                c_down_back = recon_down_back >> 1;
                c_right_half_back = recon_right_back & 0x1;
                c_down_half_back = recon_down_back & 0x1;
            }

            /* Calculate macroblock row and col. */

                mb_row = addr / (int)(_mb_width);
                mb_col = addr % (int)(_mb_width);

                /* Calculate upper left pixel row,col for luminance plane. */

                row = mb_row << 4;
                col = mb_col << 4;
                crow = row / 2;
                ccol = col / 2;

#if LOOSE_MPEG

            /* Check for illegal blocks. */

            /* Illegal forward Y block, right component */
            if (col + right_for + right_half_for + 7 > lmaxx)
            {
                if(col > lmaxx)                                  // fix mb column 
                    col = lmaxx & ~15; 
                if(col + right_for + 7 > lmaxx)                  // fix full component
                    right_for = lmaxx - col - 7;
                if(col + right_for + right_half_for + 7 > lmaxx) // fix half component
                    right_half_for = 0;
            }
            else if (col + right_for < 0)
            {
                if(col < 0)                                      // fix mb column
                    col = 0;
                if(col + right_for < 0)                          // fix full component
                    right_for = 0;
            }

            /* Illegal forward Y block, down component */
            if (row + down_for + down_half_for + 7 > lmaxy)
            {
                if(row > lmaxy)                                  // fix mb row
                    row = lmaxy & ~15; 
                if(row + down_for + 7 > lmaxy)                   // fix full component
                    down_for = lmaxy - row - 7;
                if(row + down_for + down_half_for + 7 > lmaxy)   // fix half component
                    down_half_for = 0;
            }
            else if (row + down_for < 0)
            {
                if(row < 0)                                      // fix mb row
                    row = 0;
                if(row + down_for < 0)                           // fix full component
                    down_for = 0;
            }

            /* Illegal backward Y block, right component */
            if (col + right_back + right_half_back + 7 > lmaxx)
            {
                if(col > lmaxx)                                    // fix mb column 
                    col = lmaxx & ~15; 
                if(col + right_back + 7 > lmaxx)                   // fix full component
                    right_back = lmaxx - col - 7;
                if(col + right_back + right_half_back + 7 > lmaxx) // fix half component
                    right_half_back = 0;
            }
            else if (col + right_back < 0)
            {
                if(col < 0)                                        // fix mb column
                    col = 0;
                if(col + right_back < 0)                           // fix full component
                    right_back = 0;
            }

            /* Illegal backward Y block, down component */
            if (row + down_back + down_half_back + 7 > lmaxy)
            {
                if(row > lmaxy)                                    // fix mb row
                    row = lmaxy & ~15; 
                if(row + down_back + 7 > lmaxy)                    // fix full component
                    down_back = lmaxy - row - 7;
                if(row + down_back + down_half_back + 7 > lmaxy)   // fix half component
                    down_half_back = 0;
            }
            else if (row + down_back < 0)
            {
                if(row < 0)                                        // fix mb row
                    row = 0;
                if(row + down_back < 0)                            // fix full component
                    down_back = 0;
            }


            /* Illegal forward C block, right component */
            if (ccol + c_right_for + c_right_half_for + 7 > cmaxx)
            {
                if(ccol > cmaxx)                                      // fix mb column 
                    ccol = cmaxx & ~15; 
                if(ccol + c_right_for + 7 > cmaxx)                    // fix full component
                    c_right_for = cmaxx - ccol - 7;
                if(ccol + c_right_for + c_right_half_for + 7 > cmaxx) // fix half component
                    c_right_half_for = 0;
            }
            else if (ccol + c_right_for < 0)
            {
                if(ccol < 0)                                          // fix mb column
                    ccol = 0;
                if(ccol + c_right_for < 0)                            // fix full component
                    c_right_for = 0;
            }

            /* Illegal forward C block, down component */
            if (crow + c_down_for + c_down_half_for + 7 > cmaxy)
            {
                if(crow > cmaxy)                                      // fix mb row
                    crow = cmaxy & ~15; 
                if(crow + c_down_for + 7 > cmaxy)                     // fix full component
                    c_down_for = cmaxy - crow - 7;
                if(crow + c_down_for + c_down_half_for + 7 > cmaxy)   // fix half component
                    c_down_half_for = 0;
            }
            else if (crow + c_down_for < 0)
            {
                if(crow < 0)                                           // fix mb row
                    crow = 0;
                if(crow + c_down_for < 0)                              // fix full component
                    c_down_for = 0;
            }

            /* Illegal backward C block, right component */
            if (ccol + c_right_back + c_right_half_back + 7 > cmaxx)
            {
                if(ccol > cmaxx)                                        // fix mb column 
                    ccol = cmaxx & ~15; 
                if(ccol + c_right_back + 7 > cmaxx)                     // fix full component
                    c_right_back = cmaxx - ccol - 7;
                if(ccol + c_right_back + c_right_half_back + 7 > cmaxx) // fix half component
                    c_right_half_back = 0;
            }
            else if (ccol + c_right_back < 0)
            {
                if(ccol < 0)                                            // fix mb column
                    ccol = 0;
                if(ccol + c_right_back < 0)                             // fix full component
                    c_right_back = 0;
            }

            /* Illegal backward C block, down component */
            if (crow + c_down_back + c_down_half_back + 7 > cmaxy)
            {
                if(crow > cmaxy)                                        // fix mb row
                    crow = cmaxy & ~15; 
                if(crow + c_down_back + 7 > cmaxy)                      // fix full component
                    c_down_back = cmaxy - crow - 7;
                if(crow + c_down_back + c_down_half_back + 7 > cmaxy)   // fix half component
                    c_down_half_back = 0;
            }
            else if (crow + c_down_back < 0)
            {
                if(crow < 0)                                            // fix mb row
                    crow = 0;
                if(crow + c_down_back < 0)                              // fix full component
                    c_down_back = 0;
            }
#endif

            /* If forward predicted, calculate prediction values. */
            if (mblock.bpict_past_forw)
            {
                BlockCombiner16 forw_lum = ReconSkippedBlock16(past.luminance,
                            row, col, row_size, right_for, down_for,
                            right_half_for, down_half_for);
                BlockCombiner8 forw_cr = ReconSkippedBlock8(past.Cr, crow,
                            ccol, half_row,
                            c_right_for, c_down_for, c_right_half_for, c_down_half_for);
                BlockCombiner8 forw_cb = ReconSkippedBlock8(past.Cb, crow,
                            ccol, half_row,
                            c_right_for, c_down_for, c_right_half_for, c_down_half_for);

                if (mblock.bpict_past_back)
                {
                    BlockCombiner16 back_lum = ReconSkippedBlock16(future.luminance,
                                row, col, row_size, right_back, down_back,
                                right_half_back, down_half_back);
                    BlockCombiner8 back_cr = ReconSkippedBlock8(future.Cr, crow,
                                ccol, half_row,
                                c_right_back, c_down_back,
                                c_right_half_back, c_down_half_back);
                    BlockCombiner8 back_cb = ReconSkippedBlock8(future.Cb, crow,
                                ccol, half_row,
                                c_right_back, c_down_back,
                                c_right_half_back, c_down_half_back);

                    forw_lum.AverageOut(current.luminance, (row * row_size) + col, ref back_lum, row_size);
                    forw_cr.AverageOut(current.Cr, (crow * half_row) + ccol, ref back_cr, half_row);
                    forw_cb.AverageOut(current.Cb, (crow * half_row) + ccol, ref back_cb, half_row);
                }
                else
                {
                    forw_lum.WriteOut(current.luminance, (row * row_size) + col, row_size);
                    forw_cr.WriteOut(current.Cr, (crow * half_row) + ccol, half_row);
                    forw_cb.WriteOut(current.Cb, (crow * half_row) + ccol, half_row);
                }

            }
            /* If back predicted, calculate prediction values. */
            else if (mblock.bpict_past_back)
            {
                BlockCombiner16 back_lum = ReconSkippedBlock16(future.luminance,
                            row, col, row_size, right_back, down_back,
                            right_half_back, down_half_back);
                BlockCombiner8 back_cr = ReconSkippedBlock8(future.Cr, crow,
                            ccol, half_row,
                            c_right_back, c_down_back,
                            c_right_half_back, c_down_half_back);
                BlockCombiner8 back_cb = ReconSkippedBlock8(future.Cb, crow,
                            ccol, half_row,
                            c_right_back, c_down_back,
                            c_right_half_back, c_down_half_back);

                back_lum.WriteOut(current.luminance, (row * row_size) + col, row_size);
                back_cr.WriteOut(current.Cr, (crow * half_row) + ccol, half_row);
                back_cb.WriteOut(current.Cb, (crow * half_row) + ccol, half_row);
            }
        }


        void WriteDctRowToPlane(ref DCTROW row, byte[] px, int offset)
        {
            px[offset + 0] = MPEGnet.video.BClamp(row.v0);
            px[offset + 1] = MPEGnet.video.BClamp(row.v1);
            px[offset + 2] = MPEGnet.video.BClamp(row.v2);
            px[offset + 3] = MPEGnet.video.BClamp(row.v3);
            px[offset + 4] = MPEGnet.video.BClamp(row.v4);
            px[offset + 5] = MPEGnet.video.BClamp(row.v5);
            px[offset + 6] = MPEGnet.video.BClamp(row.v6);
            px[offset + 7] = MPEGnet.video.BClamp(row.v7);
        }

        void WriteDctBlockToPlane(ref DCTBLOCK block, byte[] px, int offset, int pitch)
        {
            WriteDctRowToPlane(ref block.r0, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r1, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r2, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r3, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r4, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r5, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r6, px, offset); offset += pitch;
            WriteDctRowToPlane(ref block.r7, px, offset);
        }


        static void AccumDctRowToPlane(ref DCTROW row, byte[] dest, int destOffset, byte[] src, int srcOffset)
        {
            dest[destOffset + 0] = MPEGnet.video.BClamp(src[srcOffset + 0] + row.v0);
            dest[destOffset + 1] = MPEGnet.video.BClamp(src[srcOffset + 1] + row.v1);
            dest[destOffset + 2] = MPEGnet.video.BClamp(src[srcOffset + 2] + row.v2);
            dest[destOffset + 3] = MPEGnet.video.BClamp(src[srcOffset + 3] + row.v3);
            dest[destOffset + 4] = MPEGnet.video.BClamp(src[srcOffset + 4] + row.v4);
            dest[destOffset + 5] = MPEGnet.video.BClamp(src[srcOffset + 5] + row.v5);
            dest[destOffset + 6] = MPEGnet.video.BClamp(src[srcOffset + 6] + row.v6);
            dest[destOffset + 7] = MPEGnet.video.BClamp(src[srcOffset + 7] + row.v7);
        }

        static void AccumDctBlockToPlane(ref DCTBLOCK block, byte[] dest, int destOffset, byte[] src, int srcOffset, int pitch)
        {
            AccumDctRowToPlane(ref block.r0, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r1, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r2, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r3, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r4, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r5, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r6, dest, destOffset, src, srcOffset); srcOffset += pitch; destOffset += pitch;
            AccumDctRowToPlane(ref block.r7, dest, destOffset, src, srcOffset);
        }


        void ReconIMBlock(int mb_address, PictImage current, ref DCTBLOCK dct_recon, int bnum)
        {
            int mb_row, mb_col, row, col, row_size;
            byte[] destPlane;

            /* Calculate macroblock row and column from address. */

            mb_row = mb_address / (int)_mb_width;
            mb_col = mb_address % (int)_mb_width;

            /* If block is luminance block... */
            if (bnum < 4)
            {
                /* Calculate row and col values for upper left pixel of block. */
                row = mb_row * 16;
                col = mb_col * 16;
                if (bnum > 1)
                    row += 8;
                if (bnum % 2 != 0)
                    col += 8;

                /* Set dest to luminance plane of current pict image. */
                destPlane = current.luminance;

                /* Establish row size. */

                row_size = (int)(_mb_width * 16);
            }
            /* Otherwise if block is Cr block... */
            /* Cr first because of the earlier mixup */
            else if (bnum == 5)
            {
                /* Set dest to Cr plane of current pict image. */

                destPlane = current.Cr;

                /* Establish row size. */
                row_size = (int)(_mb_width * 8);

                /* Calculate row,col for upper left pixel of block. */
                row = mb_row * 8;
                col = mb_col * 8;
            }
            /* Otherwise block is Cb block, and ... */
            else
            {
                /* Set dest to Cb plane of current pict image. */
                destPlane = current.Cb;

                /* Establish row size. */

                row_size = (int)(_mb_width * 8);

                /* Calculate row,col for upper left pixel value of block. */

                row = mb_row * 8;
                col = mb_col * 8;
            }

            /*
             * For each pixel in block, set to cropped reconstructed value from inverse
             * dct.
             */
            WriteDctBlockToPlane(ref dct_recon, destPlane, row * row_size + col, row_size);

        }


        /*
         *--------------------------------------------------------------
         *
         * ReconBiMBlock --
         *
         *    Reconstructs bidirectionally predicted macroblocks.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        void ReconBiMBlock(int mb_address, PictImage current, PictImage past, PictImage future, ref DCTBLOCK dct_recon, int bnum,
                int recon_right_for, int recon_down_for, int recon_right_back,
                int recon_down_back, bool zflag)
        {
            int mb_row, mb_col, row, col, row_size;
            byte[] destPlane = null;
            byte[] pastPlane = null;
            byte[] futurePlane = null;

            int right_for, down_for, right_half_for, down_half_for;
            int right_back, down_back, right_half_back, down_half_back;
            int forw_row_start, back_row_start, forw_col_start, back_col_start;

#if LOOSE_MPEG
                int lmaxx = (int)(_mb_width*16-1);
                int lmaxy = (int)(_mb_height*16-1);
                int cmaxx = (int)(_mb_width*8-1);
                int cmaxy = (int)(_mb_height*8-1);
                int illegal_forw = 0;
                int illegal_back = 0;
#endif

            /* Calculate macroblock row and column from address. */

            mb_row = mb_address / (int)(_mb_width);
            mb_col = mb_address % (int)(_mb_width);

            /* If block is luminance block... */
            if (bnum < 4)
            {

                /*
                 * Calculate right_for, down_for, right_half_for, down_half_for,
                 * right_back, down_bakc, right_half_back, and down_half_back, motion
                 * vectors.
                 */

                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Set dest to luminance plane of current pict image. */

                destPlane = current.luminance;

                /* If past frame exists, set past to luminance plane of past frame. */
                if (past != null)
                    pastPlane = past.luminance;

                /*
                 * If future frame exists, set future to luminance plane of future frame.
                 */

                if (future != null)
                    futurePlane = future.luminance;

                /* Establish row size. */
                row_size = (int)(_mb_width << 4);

                /* Calculate row,col of upper left pixel in block. */

                row = (mb_row << 4);
                col = (mb_col << 4);
                if (bnum > 1)
                    row += 8;
                if ((bnum & 0x01) != 0)
                    col += 8;

#if LOOSE_MPEG
                /* Check for illegal pred. blocks. */

                /* Illegal forward Y block, right component */
                if (col + right_for + right_half_for + 7 > lmaxx)
                {
                    if(col > lmaxx)                                  // fix mb column 
                        col = lmaxx & ~15; 
                    if(col + right_for + 7 > lmaxx)                  // fix full component
                        right_for = lmaxx - col - 7;
                    if(col + right_for + right_half_for + 7 > lmaxx) // fix half component
                        right_half_for = 0;
                }
                else if (col + right_for < 0)
                {
                    if(col < 0)                                      // fix mb column
                        col = 0;
                    if(col + right_for < 0)                          // fix full component
                        right_for = 0;
                }

                /* Illegal forward Y block, down component */
                if (row + down_for + down_half_for + 7 > lmaxy)
                {
                    if(row > lmaxy)                                  // fix mb row
                        row = lmaxy & ~15; 
                    if(row + down_for + 7 > lmaxy)                   // fix full component
                        down_for = lmaxy - row - 7;
                    if(row + down_for + down_half_for + 7 > lmaxy)   // fix half component
                        down_half_for = 0;
                }
                else if (row + down_for < 0)
                {
                    if(row < 0)                                      // fix mb row
                        row = 0;
                    if(row + down_for < 0)                           // fix full component
                        down_for = 0;
                }


                /* Illegal backward Y block, right component */
                if (col + right_back + right_half_back + 7 > lmaxx)
                {
                    if(col > lmaxx)                                    // fix mb column 
                        col = lmaxx & ~15; 
                    if(col + right_back + 7 > lmaxx)                   // fix full component
                        right_back = lmaxx - col - 7;
                    if(col + right_back + right_half_back + 7 > lmaxx) // fix half component
                        right_half_back = 0;
                }
                else if (col + right_back < 0)
                {
                    if(col < 0)                                        // fix mb column
                        col = 0;
                    if(col + right_back < 0)                           // fix full component
                        right_back = 0;
                }

                /* Illegal backward Y block, down component */
                if (row + down_back + down_half_back + 7 > lmaxy)
                {
                    if(row > lmaxy)                                    // fix mb row
                        row = lmaxy & ~15; 
                    if(row + down_back + 7 > lmaxy)                    // fix full component
                        down_back = lmaxy - row - 7;
                    if(row + down_back + down_half_back + 7 > lmaxy)   // fix half component
                        down_half_back = 0;
                }
                else if (row + down_back < 0)
                {
                    if(row < 0)                                        // fix mb row
                        row = 0;
                    if(row + down_back < 0)                            // fix full component
                        down_back = 0;
                }
#endif

                forw_col_start = col + right_for;
                forw_row_start = row + down_for;

                back_col_start = col + right_back;
                back_row_start = row + down_back;

            }
            /* Otherwise, block is NOT luminance block, ... */
            else
            {
                /* Construct motion vectors. */

                recon_right_for /= 2;
                recon_down_for /= 2;
                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                recon_right_back /= 2;
                recon_down_back /= 2;
                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Establish row size. */
                row_size = (int)(_mb_width << 3);

                /* Calculate row,col of upper left pixel in block. */

                row = (mb_row << 3);
                col = (mb_col << 3);

#if LOOSE_MPEG

                /* Check for illegal pred. blocks. */


                /* Illegal forward C block, right component */
                if (col + right_for + right_half_for + 7 > cmaxx)
                {
                    if(col > cmaxx)                                  // fix mb column 
                        col = cmaxx & ~15; 
                    if(col + right_for + 7 > cmaxx)                  // fix full component
                        right_for = cmaxx - col - 7;
                    if(col + right_for + right_half_for + 7 > cmaxx) // fix half component
                        right_half_for = 0;
                }
                else if (col + right_for < 0)
                {
                    if(col < 0)                                      // fix mb column
                        col = 0;
                    if(col + right_for < 0)                          // fix full component
                        right_for = 0;
                }
    
                /* Illegal forward C block, down component */
                if (row + down_for + down_half_for + 7 > cmaxy)
                {
                    if(row > cmaxy)                                  // fix mb row
                        row = cmaxy & ~15; 
                    if(row + down_for + 7 > cmaxy)                   // fix full component
                        down_for = cmaxy - row - 7;
                    if(row + down_for + down_half_for + 7 > cmaxy)   // fix half component
                        down_half_for = 0;
                }
                else if (row + down_for < 0)
                {
                    if(row < 0)                                      // fix mb row
                        row = 0;
                    if(row + down_for < 0)                           // fix full component
                        down_for = 0;
                }


                /* Illegal backward C block, right component */
                if (col + right_back + right_half_back + 7 > cmaxx)
                {
                    if(col > cmaxx)                                    // fix mb column 
                        col = cmaxx & ~15; 
                    if(col + right_back + 7 > cmaxx)                   // fix full component
                        right_back = cmaxx - col - 7;
                    if(col + right_back + right_half_back + 7 > cmaxx) // fix half component
                        right_half_back = 0;
                }
                else if (col + right_back < 0)
                {
                    if(col < 0)                                        // fix mb column
                        col = 0;
                    if(col + right_back < 0)                           // fix full component
                        right_back = 0;
                }

                /* Illegal backward C block, down component */
                if (row + down_back + down_half_back + 7 > cmaxy)
                {
                    if(row > cmaxy)                                    // fix mb row
                        row = cmaxy & ~15; 
                    if(row + down_back + 7 > cmaxy)                    // fix full component
                        down_back = cmaxy - row - 7;
                    if(row + down_back + down_half_back + 7 > cmaxy)   // fix half component
                        down_half_back = 0;
                }
                else if (row + down_back < 0)
                {
                    if(row < 0)                                        // fix mb row
                        row = 0;
                    if(row + down_back < 0)                            // fix full component
                        down_back = 0;
                }
#endif

                forw_col_start = col + right_for;
                forw_row_start = row + down_for;

                back_col_start = col + right_back;
                back_row_start = row + down_back;

                /* If block is Cr block... */
                /* Switched earlier, so we test Cr first - eyhung */

                if (bnum == 5)
                {
                    /* Set dest to Cr plane of current pict image. */
                    destPlane = current.Cr;

                    /* If past frame exists, set past to Cr plane of past image. */
                    if (past != null)
                        pastPlane = past.Cr;

                    /*
                     * If future frame exists, set future to Cr plane of future image.
                     */
                    if (future != null)
                        futurePlane = future.Cr;
                }
                /* Otherwise, block is Cb block... */
                else
                {
                    /* Set dest to Cb plane of current pict image. */
                    destPlane = current.Cb;

                    /* If past frame exists, set past to Cb plane of past frame. */

                    if (past != null)
                        pastPlane = past.Cb;

                    /*
                     * If future frame exists, set future to Cb plane of future frame.
                     */

                    if (future != null)
                        futurePlane = future.Cb;
                }
            }

            /* For each pixel in block... */

            int destIndex = (row * row_size) + col;

            ByteCursor rindex1;
            ByteCursor bindex1;
#if LOOSE_MPEG
            if (illegal_forw != 0) 
                rindex1 = new ByteCursor(futurePlane, back_row_start * row_size + back_col_start);
            else 
#endif
            rindex1 = new ByteCursor(pastPlane, forw_row_start * row_size + forw_col_start);

#if LOOSE_MPEG
            if (illegal_back != 0) 
                bindex1 = new ByteCursor(pastPlane, forw_row_start * row_size + forw_col_start);
            else 
#endif
            bindex1 = new ByteCursor(futurePlane, back_row_start * row_size + back_col_start);

            if (!zflag)
                Transforms.Transform_Avg2_AddDct_CAW(row_size, rindex1.Array, rindex1.Address, bindex1.Array, bindex1.Address, ref dct_recon, destPlane, destIndex);
            else
                Transforms.Transform_Avg2_Write(row_size, rindex1.Array, rindex1.Address, bindex1.Array, bindex1.Address, destPlane, destIndex);
        }


        /*
         *--------------------------------------------------------------
         *
         * ReconPMBlock --
         *
         *    Reconstructs forward predicted macroblocks.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        void ReconPMBlock(int mb_address, PictImage current, PictImage past, PictImage future, ref DCTBLOCK dct_recon,
            int bnum, int recon_right_for, int recon_down_for, int picture_code_type, bool zflag)
        {
            int mb_row, mb_col, row, col, row_size;
            byte[] destPlane;
            byte[] pastPlane = null;
            int right_for, down_for, right_half_for, down_half_for;

#if LOOSE_MPEG
            int maxx, maxy;
            int illegalBlock = 0;
#endif

            /* Calculate macroblock row and column from address. */
            mb_row = mb_address / (int)_mb_width;
            mb_col = mb_address % (int)_mb_width;

            if (bnum < 4)
            {
                /* Calculate right_for, down_for motion vectors. */

                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                /* Set dest to luminance plane of current pict image. */
                destPlane = current.luminance;
                if (picture_code_type == VidStream.B_TYPE)
                {
                    if (past != null)
                        pastPlane = past.luminance;
                }
                else
                {
                    /* Set predictive frame to current future frame. */
                    if (future != null)
                        pastPlane = future.luminance;
                }

                /* Establish row size. */

                row_size = (int)(_mb_width << 4);
                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 4;
                col = mb_col << 4;
                if (bnum > 1)
                    row += 8;
                if (bnum % 2 != 0)
                    col += 8;

#if LOOSE_MPEG
                /* Check for block illegality. */

                maxx = (int)(_mb_width * 16 - 1);
                maxy = (int)(_mb_height * 16 - 1);

                if (row + down_for + down_half_for + 7 > maxy)
                    illegalBlock |= 0x4;
                else if (row + down_for < 0)
                    illegalBlock |= 0x1;

                if (col + right_for + right_half_for + 7 > maxx)
                    illegalBlock |= 0x2;
                else if (col + right_for < 0)
                    illegalBlock |= 0x8;

#endif
            }
            /* Otherwise, block is NOT luminance block, ... */
            else
            {
                /* Construct motion vectors. */
                recon_right_for /= 2;
                recon_down_for /= 2;
                right_for = recon_right_for >> 1;
                down_for = recon_down_for >> 1;
                right_half_for = recon_right_for & 0x1;
                down_half_for = recon_down_for & 0x1;

                /* Establish row size. */

                row_size = (int)(_mb_width << 3);

                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 3;
                col = mb_col << 3;

#if LOOSE_MPEG
                /* Check for block illegality. */

                maxx = (int)(_mb_width * 8 - 1);
                maxy = (int)(_mb_height * 8 - 1);

                if (row + down_for + down_half_for + 7 > maxy)
                    illegalBlock |= 0x4;
                else if (row + down_for < 0)
                    illegalBlock |= 0x1;

                if (col + right_for + right_half_for + 7 > maxx)
                    illegalBlock |= 0x2;
                else if (col + right_for < 0)
                    illegalBlock |= 0x8;

#endif

                /* If block is Cr block... */
                /* 5 first because order was mixed up in earlier versions */

                if (bnum == 5)
                {

                    /* Set dest to Cr plane of current pict image. */

                    destPlane = current.Cr;

                    if (picture_code_type == VidStream.B_TYPE)
                    {
                        if (past != null)
                            pastPlane = past.Cr;
                    }
                    else
                    {
                        if (future != null)
                            pastPlane = future.Cr;
                    }
                }
                /* Otherwise, block is Cb block... */
                else
                {
                    /* Set dest to Cb plane of current pict image. */
                    destPlane = current.Cb;
                    if (picture_code_type == VidStream.B_TYPE)
                    {
                        if (past != null)
                            pastPlane = past.Cb;
                    }
                    else
                    {
                        if (future != null)
                            pastPlane = future.Cb;
                    }
                }
            }

            /* For each pixel in block... */

#if LOOSE_MPEG
            if (illegalBlock == 0)
            {
#endif

                int destIndex = (row * row_size) + col;
                int pastRIndex1 = (row + down_for) * row_size + col + right_for;

                /*
                 * Calculate predictive pixel value based on motion vectors and copy to
                 * dest plane.
                 */
                if ((down_half_for == 0) && (right_half_for == 0))
                {
                    if (!zflag)
                        AccumDctBlockToPlane(ref dct_recon, destPlane, destIndex, pastPlane, pastRIndex1, row_size);
                    else
                    {
                        for (int rr = 0; rr < 8; rr++)
                        {
                            Array.Copy(pastPlane, pastRIndex1, destPlane, destIndex, 8);
                            destIndex += row_size;
                            pastRIndex1 += row_size;
                        }
                    }
                }
                else
                {
                    int pastRIndex2 = pastRIndex1 + right_half_for + (down_half_for * row_size);

                    /* if one of the two is zero, then quality makes no difference */
                    if ((right_half_for == 0) ||
                        (down_half_for == 0) || (!QualityFlag))
                    {
                        if (!zflag)
                        {
                            // index = crop((rindex1[0] + rindex2[0] + 1) >> 1) + blockvals[0]
                            Transforms.Transform_Avg2_AddDct_CAW(row_size, pastPlane, pastRIndex1, pastPlane, pastRIndex2, ref dct_recon, destPlane, destIndex);
                        }
                        else
                        {
                            /* zflag */
                            Transforms.Transform_Avg2_Write(row_size, pastPlane, pastRIndex1, pastPlane, pastRIndex2, destPlane, destIndex);
                        }
                    }
                    else
                    {
                        /* qualityFlag on and both vectors are non-zero */
                        int pastRIndex3 = pastRIndex1 + right_half_for;
                        int pastRIndex4 = pastRIndex1 + (down_half_for * row_size);
                        if (!zflag)
                            Transforms.Transform_Avg4_AddDct_CAW(row_size, pastPlane, pastRIndex1, pastRIndex2, pastRIndex3, pastRIndex4, ref dct_recon, destPlane, destIndex);
                        else
                        {
                            /* zflag */
                            Transforms.Transform_Avg4_Write(row_size, pastPlane, pastRIndex1, pastRIndex2, pastRIndex3, pastRIndex4, destPlane, destIndex);
                        }
                    }

                }
#if LOOSE_MPEG
            }
#endif
        }


        /*
         *--------------------------------------------------------------
         *
         * ReconBMBlock --
         *
         *    Reconstructs back predicted macroblocks.
         *
         * Results:
         *    None.
         *
         * Side effects:
         *    None.
         *
         *--------------------------------------------------------------
         */

        void ReconBMBlock(int mb_address, PictImage current, PictImage past, PictImage future, ref DCTBLOCK dct_recon,
            int bnum, int recon_right_back, int recon_down_back, bool zflag)
        {
            int mb_row, mb_col, row, col, row_size;
            int right_back, down_back, right_half_back, down_half_back;
            byte[] destPlane = null;
            byte[] futurePlane = null;

#if LOOSE_MPEG
            int illegalBlock = 0;
#endif

            /* Calculate macroblock row and column from address. */
            mb_row = mb_address / (int)_mb_width;
            mb_col = mb_address % (int)_mb_width;

            /* If block is luminance block... */

            if (bnum < 4)
            {
                /* Calculate right_back, down_back motion vectors. */

                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Set dest to luminance plane of current pict image. */

                destPlane = current.luminance;

                /*
                 * If future frame exists, set future to luminance plane of future frame.
                 */
                if (future != null)
                    futurePlane = future.luminance;

                /* Establish row size. */
                row_size = (int)(_mb_width << 4);

                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 4;
                col = mb_col << 4;
                if (bnum > 1)
                    row += 8;
                if ((bnum % 2) != 0)
                    col += 8;

#if LOOSE_MPEG
                /* Check for block illegality. */

                int maxx = (int)(_mb_width * 16 - 1);
                int maxy = (int)(_mb_height * 16 - 1);

                if (row + down_back + down_half_back + 7 > maxy)
                    illegalBlock |= 0x4;
                else if (row + down_back < 0)
                    illegalBlock |= 0x1;

                if (col + right_back + right_half_back + 7 > maxx)
                    illegalBlock |= 0x2;
                else if (col + right_back < 0)
                    illegalBlock |= 0x8;

#endif
            }
            /* Otherwise, block is NOT luminance block, ... */
            else
            {
                /* Construct motion vectors. */
                recon_right_back /= 2;
                recon_down_back /= 2;
                right_back = recon_right_back >> 1;
                down_back = recon_down_back >> 1;
                right_half_back = recon_right_back & 0x1;
                down_half_back = recon_down_back & 0x1;

                /* Establish row size. */

                row_size = (int)(_mb_width << 3);

                /* Calculate row,col of upper left pixel in block. */

                row = mb_row << 3;
                col = mb_col << 3;

#if LOOSE_MPEG

                /* Check for block illegality. */

                int maxx = (int)(_mb_width * 8 - 1);
                int maxy = (int)(_mb_height * 8 - 1);

                if (row + down_back + down_half_back + 7 > maxy)
                    illegalBlock |= 0x4;
                else if (row + down_back < 0)
                    illegalBlock |= 0x1;

                if (col + right_back + right_half_back + 7 > maxx)
                    illegalBlock |= 0x2;
                else if (col + right_back < 0)
                    illegalBlock |= 0x8;
#endif

                /* If block is Cr block... */
                /* They were switched earlier, so 5 is first - eyhung */

                if (bnum == 5)
                {
                    /* Set dest to Cr plane of current pict image. */
                    destPlane = current.Cr;

                    /*
                     * If future frame exists, set future to Cr plane of future image.
                     */

                    if (future != null)
                        futurePlane = future.Cr;
                }
                /* Otherwise, block is Cb block... */
                else
                {
                    /* Set dest to Cb plane of current pict image. */
                    destPlane = current.Cb;

                    /*
                     * If future frame exists, set future to Cb plane of future frame.
                     */
                    if (future != null)
                        futurePlane = future.Cb;
                }
            }

            /* For each pixel in block do... */

#if LOOSE_MPEG
            if (illegalBlock == 0)
            {
#endif

                int destIndex = (row * row_size) + col;
                int futureRIndex1 = (row + down_back) * row_size + col + right_back;

                if ((right_half_back == 0) && (down_half_back == 0))
                {
                    if (!zflag)
                    {
                        Transforms.Transform_AddDct_CAW(row_size, futurePlane, futureRIndex1, ref dct_recon, destPlane, destIndex);
                    }
                    else
                    {
                        for (int rr = 0; rr < 8; rr++)
                        {
                            Array.Copy(futurePlane, futureRIndex1, destPlane, destIndex, 8);
                            destIndex += row_size;
                            futureRIndex1 += row_size;
                        }
                    }
                }
                else
                {
                    int futureRIndex2 = futureRIndex1 + right_half_back + (down_half_back * row_size);
                    if (!QualityFlag)
                    {
                        if (!zflag)
                        {
                            Transforms.Transform_Avg2_AddDct_CAW(row_size, futurePlane, futureRIndex1, futurePlane, futureRIndex2, ref dct_recon, destPlane, destIndex);
                        }
                        else
                        {
                            /* zflag */
                            Transforms.Transform_Avg2_Write(row_size, futurePlane, futureRIndex1, futurePlane, futureRIndex2, destPlane, destIndex);
                        }
                    }
                    else
                    {
                        /* qualityFlag on */
                        int futureRIndex3 = futureRIndex1 + right_half_back;
                        int futureRIndex4 = futureRIndex1 + (down_half_back * row_size);
                        if (!zflag)
                        {
                            Transforms.Transform_Avg4_AddDct_CAW(row_size, futurePlane, futureRIndex1, futureRIndex2, futureRIndex3, futureRIndex4, ref dct_recon, destPlane, destIndex);
                        }
                        else
                        {
                            /* zflag */
                            Transforms.Transform_Avg4_Write(row_size, futurePlane, futureRIndex1, futureRIndex2, futureRIndex3, futureRIndex4, destPlane, destIndex);
                        }
                    }
                }
#if LOOSE_MPEG
            }
#endif
        }
    }
}
